/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file hubctrl.c
 * \brief HubCtrl protocol handler module - implementation.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 */
#include "hubctrl.h"
#include "owndebug.h"
#include "ownutils.h"
#include "hubctrl_common.h"
#include "hubctrl_process.h"
#include <assert.h>
#include <stdlib.h>


//*****************************************************************************
//*****************************************************************************
static EProtocolExit hubctrlErrorHandler( THubCtrl *hc, 
                                          TConnectionList *l, 
                                          TConnectionListEntry *e );
static EProtocolExit hubctrlHandler(THubCtrl *hc,
                                    TConnectionList *l, 
                                    TConnectionListEntry *e);
static EProtocolExit dispatchPacket(THubCtrl *hc,
                                    TConnectionList *l,
                                    TConnectionListEntry *e,
                                    const THubCtrlProtocolHeader *hdr,
                                    const unsigned int total_bytes);
//*****************************************************************************

//*****************************************************************************

THubCtrl *hubctrlCreate()
{
  THubCtrl *hc = (THubCtrl *)malloc(sizeof(THubCtrl));
  assert(hc != NULL);

  if (hubctrlInit(hc) != HUBCTRL_OK) {
    free(hc);
    hc = NULL;
  }
  dPrint(6,"Hub Control Protocol (HubCtrl) - Created");
  return hc;
}
//*****************************************************************************

int hubctrlInit(THubCtrl *hc)
{
  dPrint(2,"Hub Control Protocol (HubCtrl) - Initialized");
  dPrint(3,"Sizeof HubCtrlHeader is %d bytes", sizeof(THubCtrlProtocolHeader));
  
  memset(hc, 0x00, sizeof(hc));

  // Set default secrets
  hubctrlSetAP2APSecret(hc, (void*)"apnone", 6);
  hubctrlSetAPServiceSecret(hc, (void*)"apsnone", 7);
  hubctrlSetClientSecret(hc, (void*)"clientnone", 10);

  hubctrlSetMaxTimestampDifference(hc, 10*60);


  // Init reserved to defaults
  hc->APs_reserved_pool_begin = 0x01;
  hc->APs_reserved_pool_end = 0x0F;

  return HUBCTRL_OK;
}
//*****************************************************************************

void hubctrlDeinit(THubCtrl *hc)
{
  if (hc->ap2ap_secret) free(hc->ap2ap_secret);
  dPrint(4,"Hub Control Protocol (HubCtrl) - Deinitialized");
  return;
}
//*****************************************************************************

void hubctrlDestroy(THubCtrl *hc)
{
  free(hc);
  dPrint(5,"Hub Control Protocol (HubCtrl) - Destroyed");
  return;
}
//*****************************************************************************

const TProtocolEntry *hubctrlGetProtocolEntry(THubCtrl *hc)
{
  TProtocolEntry *ne;
  assert(hc != NULL);

  ne = protocolEntryConstructNew(HUBCTRLPROTOCOL_NUMBER,
                                 hc,
                                 (protocolRawHandlerPtr)NULL,
                                 (protocolHandlerPtr)hubctrlHandler,
                                 (protocolErrorHandlerPtr)hubctrlErrorHandler);
  
  // Return the new entry!
  return ne;
}
//*****************************************************************************

void hubctrlDumpProtocolHeader(int l, const THubCtrlProtocolHeader *ph)
{
  // Dump the protocol header part
  protocolIdentificationDumpHeader(l, &ph->protocol_id);

  // Some basic tests
  if ( ph->protocol_id.protocol_number != HUBCTRLPROTOCOL_NUMBER ) 
    dPrint(1,
           "WARNING: Received packet doesn't appear to be HUBCTRLPROTOCOL "
           "packet (should be 0x%02x)",
           HUBCTRLPROTOCOL_NUMBER);
 
  // Print
  dPrint(l,
         "HubCtrl packet:\n"
         "\trequest:\t0x%02x\n"
         "\tflags:\t\t0x%02x\n",
         ph->request,
         ph->flags);
}
//*****************************************************************************

static EProtocolExit hubctrlErrorHandler( THubCtrl *hc, 
                                          TConnectionList *l, 
                                          TConnectionListEntry *e )
{
  assert(hc != NULL);
  assert(l != NULL);
  assert(e != NULL);
  
  dPrint(3, "Hubctrl Error handler called!");
  
  // Nothing to to currently.

  return protocolExitNoError;
}
//*****************************************************************************

static EProtocolExit hubctrlHandler(THubCtrl *hc,
                                    TConnectionList *l, 
                                    TConnectionListEntry *e)
{
  EProtocolExit pExit = protocolExitError;
  THubCtrlProtocolHeader *hdr;
  const unsigned char *dataBuffer;
  int dataBytes;

  // Checks
  assert(hc != NULL);
  assert(l != NULL);
  assert(e != NULL);

  // Get easier-to-reference pointers.
  dataBuffer = bufferGetPtr(&e->inputBuffer); // Data buffer
  dataBytes = bufferGetLength(&e->inputBuffer); // Whole buffer len
  
  // Check if we got the whole header
  assert(dataBytes >= sizeof(THubCtrlProtocolHeader));
  
  // Typecast the data in struct form for easy data access.
  hdr = (THubCtrlProtocolHeader *)dataBuffer;
      
  // Dump for debug
  hubctrlDumpProtocolHeader(8, hdr);
      
  // Check that the protocol number&version matches.
  if (hdr->protocol_id.protocol_number != HUBCTRLPROTOCOL_NUMBER) {
    dPrint(1,"ERROR: Invalid magic number received! (%02x != %02x)",
           hdr->protocol_id.protocol_number, HUBCTRLPROTOCOL_NUMBER);
    return protocolExitError;
    
  }

  // The packet is complete! So handle it!
  pExit = dispatchPacket(hc, l, e, hdr, dataBytes);

  return pExit;
}
//*****************************************************************************

void hubctrlSetAP2APSecret(THubCtrl *hc, const uint8_t *secret, 
                         const unsigned int secret_size)
{
  if (hc->ap2ap_secret) free(hc->ap2ap_secret);
  hc->ap2ap_secret_size = secret_size;
  hc->ap2ap_secret = malloc(secret_size);
  memcpy(hc->ap2ap_secret, secret, secret_size);
}
//*****************************************************************************

void hubctrlSetAPServiceSecret(THubCtrl *hc, const uint8_t *secret, 
                               const unsigned int secret_size)
{
  if (hc->APs_secret) free(hc->APs_secret);
  hc->APs_secret_size = secret_size;
  hc->APs_secret = malloc(secret_size);
  memcpy(hc->APs_secret, secret, secret_size);
}
//*****************************************************************************

void hubctrlSetClientSecret(THubCtrl *hc, const uint8_t *secret, 
                            const unsigned int secret_size)
{
  if (hc->client_secret) free(hc->client_secret);
  hc->client_secret_size = secret_size;
  hc->client_secret = malloc(secret_size);
  memcpy(hc->client_secret, secret, secret_size);
}
//*****************************************************************************

void hubctrlSetMaxTimestampDifference(THubCtrl *hc, const unsigned int timestamp_difference_max)
{
  hc->timestamp_difference_max = timestamp_difference_max;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

static EProtocolExit dispatchPacket(THubCtrl *hc,
                                    TConnectionList *l,
                                    TConnectionListEntry *e,
                                    const THubCtrlProtocolHeader *hdr,
                                    const unsigned int total_bytes)
{
  EProtocolExit result = protocolExitError;

  uint8_t replybuffer[4096]; ///< Currently enough for everything
  unsigned int replybuffer_datasize = 0; ///< Stored data to send

  // Construct a processQueryData structure to avoid overhead of
  // copying all data in registers.
  struct SProcessQueryData pqd = { l, e, hdr, total_bytes,
                                   replybuffer, sizeof(replybuffer),
                                   &replybuffer_datasize, 
                                   NULL, 0 };
  

  // Check integrity
  if (hubctrlcommonGetSubHeaders(hdr, total_bytes, &pqd.subhdrtable, &pqd.subhdrcount) == 0) {

    // Switch & Act by request type
    switch(hdr->request) {
    case KHubCtrlRequestQueryLocalNodeId: {
      if (hdr->flags & KHubCtrlFlagReply) {
      } else {
        result = processRequestQueryLocalNodeId(hc, &pqd);
      }
      break;
    }
      
    case KHubCtrlRequestServerEcho: {
      if (hdr->flags & KHubCtrlFlagReply) {
      } else {
        result = processRequestServerEcho(hc, &pqd);
      }
      break;
    }
      
    case KHubCtrlRequestHubVersion: {
      if (hdr->flags & KHubCtrlFlagReply) {
      } else {
        result = processRequestHubVersion(hc, &pqd);
      }
      break;
    }
      
    case KHubCtrlRequestRegisterAP: {
      if (hdr->flags & KHubCtrlFlagReply) {
        result = processRequestRegisterAPReply(hc, &pqd);

      } else {
        result = processRequestRegisterAP(hc, &pqd);

      }
      break;
    }
      
    case KHubCtrlRequestRegisterAPService: {
      if (hdr->flags & KHubCtrlFlagReply) {
      } else {
        result = processRequestRegisterAPService(hc, &pqd);
      }
      break;
    }
      
    case KHubCtrlRequestRegisterAPClient: {
      if (hdr->flags & KHubCtrlFlagReply) {
      } else {
        result = processRequestRegisterAPClient(hc, &pqd);
      }
      break;
    }

    default: {
      result = processRequestUnknown(hc, &pqd);
      break;
    }
    }

    // Release subheaders table if such exists
    if (pqd.subhdrtable) free(pqd.subhdrtable);

  } else {
    // Process as unknown
    result = processRequestUnknown(hc, &pqd);

  }  


  // Send the reply!
  if (replybuffer_datasize) {
    result = connectionListSendRawDataByEntry(l, 
                                              (const char *)replybuffer, 
                                              replybuffer_datasize,
                                              e);
    if (result != CONNECTIONLIST_OK) {
      dPrint(7, "Warning; The SendRawDataByEntry has failed!");
      return protocolExitError;
      
    } else {
      return protocolExitNoError;
    }
  } else {
    dPrint(4,"No reply generated for command %d", hdr->request);
    return protocolExitNoError;
  }
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************

